"""
Swift 符号提取器

基于 swiftc 编译器的 AST 输出提取 Swift 符号。
"""

import os
import re
import subprocess
from typing import Dict, List, Optional
from pathlib import Path

from core.extractors_module.base_extractor import LanguageExtractor
from core.interfaces_module.extractor import SymbolInfo


class SwiftExtractor(LanguageExtractor):
    """
    Swift 符号提取器

    使用 swiftc -dump-ast 获取精确的符号信息。
    支持:
    - 类 (class)
    - 结构体 (struct)
    - 枚举 (enum)
    - 协议 (protocol)
    - 函数/方法
    - 属性
    """

    def get_language_name(self) -> str:
        """获取语言名称"""
        return "swift"

    def get_compiler_command(self) -> str:
        """获取编译器命令"""
        return "swiftc"

    def get_file_extensions(self) -> List[str]:
        """获取文件扩展名"""
        return ['.swift']

    def extract_symbols_from_file(self, file_path: str) -> Dict:
        """
        从单个 Swift 文件提取符号

        Args:
            file_path: 文件路径

        Returns:
            符号字典
        """
        return self._extract_swift_single_file(file_path)

    def extract_symbols_batch(self, files: List[str]) -> Dict:
        """
        批量提取 Swift 符号

        Args:
            files: Swift 文件列表

        Returns:
            合并后的符号字典
        """
        if not files:
            return {}

        # 检查编译器可用性
        if not self.is_available():
            # 编译器不可用，检查配置
            if hasattr(self.config, 'analysis') and self.config.analysis.require_ast:
                error_msg = (
                    "Swift 编译器未找到，且配置要求使用 AST 提取。\n"
                    "请安装 swiftc 或设置 SWIFTC_PATH 环境变量。"
                )
                self.logger.log_error(error_msg)
                raise RuntimeError(error_msg)

            # 允许降级到正则提取
            if hasattr(self.config, 'analysis') and self.config.analysis.allow_fallback:
                if hasattr(self.config, 'analysis') and self.config.analysis.log_fallback:
                    self.logger.log_warning(
                        "⚠️  降级到正则提取 Swift 符号\n"
                        "   原因: Swift 编译器不可用\n"
                        "   影响: 符号提取精度降低至 70-80%\n"
                        "   建议: 安装 Swift 编译器以获得最佳效果"
                    )
                return self.extract_symbols_by_regex(files)

        self.logger.log_operation("Swift AST 分析", f"处理 {len(files)} 个文件")

        symbols = self._init_symbol_dict(files)

        # 分批处理，避免命令行过长
        batch_size = 20
        for i in range(0, len(files), batch_size):
            batch_files = files[i:i + batch_size]
            batch_symbols = self._extract_swift_batch(batch_files)
            self._merge_symbols(symbols, batch_symbols)

        return symbols

    def _init_symbol_dict(self, files: List[str] = None) -> Dict:
        """初始化符号字典"""
        return {
            'classes': {},
            'structs': {},
            'enums': {},
            'protocols': {},
            'functions': {},
            'properties': {},
            'methods': {},
            'file_symbols': {f: [] for f in (files or [])}
        }

    def _extract_swift_batch(self, files: List[str]) -> Dict:
        """
        提取一批 Swift 文件的符号

        Args:
            files: 文件列表

        Returns:
            符号信息
        """
        symbols = self._init_symbol_dict(files)

        # 构建编译命令
        cmd = [self.get_compiler_path(), '-dump-ast']

        # 添加文件路径
        file_paths = []
        for f in files:
            if isinstance(f, str):
                file_paths.append(f)
            elif isinstance(f, Path):
                file_paths.append(str(f))
            else:
                self.logger.log_warning(f"忽略非字符串文件路径: {type(f)}")
        cmd.extend(file_paths)

        try:
            # 执行编译器获取 AST
            result = self._run_compiler(['-dump-ast'] + file_paths, timeout=30)

            # Swift AST 输出（新版在 stdout，旧版在 stderr）
            if result.returncode == 0:
                ast_output = result.stdout if result.stdout else result.stderr
                if ast_output and len(ast_output) > 10:
                    symbols = self._parse_swift_ast(ast_output, file_paths)
                else:
                    self.logger.log_warning("Swift AST 输出为空")
            else:
                error_msg = result.stderr if result.stderr else result.stdout
                self.logger.log_warning(f"Swift 编译失败: {error_msg[:200]}")

        except subprocess.TimeoutExpired:
            self.logger.log_warning("Swift AST 分析超时，尝试单文件处理")
            # 降级为单文件处理
            for file_path in file_paths:
                file_symbols = self._extract_swift_single_file(file_path)
                self._merge_symbols(symbols, file_symbols)

        except Exception as e:
            self.logger.log_error(f"Swift AST 分析失败: {e}")

        return symbols

    def extract_symbols_by_regex(self, files: List[str]) -> Dict:
        """
        使用正则表达式提取 Swift 符号（公共接口）

        Args:
            files: Swift 文件列表

        Returns:
            符号字典
        """
        return self._extract_swift_symbols_by_regex(files)

    def _extract_swift_single_file(self, file_path: str) -> Dict:
        """
        提取单个 Swift 文件的符号

        Args:
            file_path: 文件路径

        Returns:
            符号信息
        """
        symbols = {
            'classes': {},
            'structs': {},
            'enums': {},
            'protocols': {},
            'functions': {},
            'properties': {},
            'file_symbols': {file_path: []}
        }

        try:
            result = self._run_compiler(['-dump-ast', file_path], timeout=15)

            if result.returncode == 0:
                ast_output = result.stdout if result.stdout else result.stderr
                if ast_output:
                    symbols = self._parse_swift_ast(ast_output, [file_path])

        except Exception as e:
            self.logger.log_warning(f"Swift 单文件分析失败 {file_path}: {e}")

        return symbols

    def _parse_swift_ast(self, ast_output: str, files: List[str]) -> Dict:
        """
        解析 Swift AST 输出

        Args:
            ast_output: AST 输出文本
            files: 相关文件列表

        Returns:
            解析的符号信息
        """
        symbols = self._init_symbol_dict(files)

        lines = ast_output.split('\n')
        current_file = None
        current_class = None

        for line in lines:
            # 提取文件信息
            if 'source_file' in line:
                file_match = re.search(r'"([^"]+\.swift)"', line)
                if file_match:
                    file_path = file_match.group(1)
                    for f in files:
                        if f.endswith(os.path.basename(file_path)):
                            current_file = f
                            break

            # 解析类声明
            if 'class_decl' in line:
                symbol = self._extract_swift_class(line, current_file)
                if symbol:
                    symbols['classes'][symbol.name] = symbol
                    current_class = symbol.name
                    if current_file and current_file in symbols['file_symbols']:
                        symbols['file_symbols'][current_file].append(symbol.name)

            # 解析结构体声明
            elif 'struct_decl' in line:
                symbol = self._extract_swift_struct(line, current_file)
                if symbol:
                    symbols['structs'][symbol.name] = symbol
                    current_class = symbol.name
                    if current_file and current_file in symbols['file_symbols']:
                        symbols['file_symbols'][current_file].append(symbol.name)

            # 解析枚举声明
            elif 'enum_decl' in line:
                symbol = self._extract_swift_enum(line, current_file)
                if symbol:
                    symbols['enums'][symbol.name] = symbol
                    if current_file and current_file in symbols['file_symbols']:
                        symbols['file_symbols'][current_file].append(symbol.name)

            # 解析协议声明
            elif 'protocol_decl' in line:
                symbol = self._extract_swift_protocol(line, current_file)
                if symbol:
                    symbols['protocols'][symbol.name] = symbol
                    if current_file and current_file in symbols['file_symbols']:
                        symbols['file_symbols'][current_file].append(symbol.name)

            # 解析函数声明
            elif 'func_decl' in line:
                symbol = self._extract_swift_function(line, current_file, current_class)
                if symbol:
                    if current_class:
                        symbols['methods'][f"{current_class}.{symbol.name}"] = symbol
                    else:
                        symbols['functions'][symbol.name] = symbol
                    if current_file and current_file in symbols['file_symbols']:
                        symbols['file_symbols'][current_file].append(symbol.name)

            # 解析属性声明
            elif 'var_decl' in line or 'pattern_binding_decl' in line:
                symbol = self._extract_swift_property(line, current_file, current_class)
                if symbol:
                    symbols['properties'][symbol.name] = symbol
                    if current_file and current_file in symbols['file_symbols']:
                        symbols['file_symbols'][current_file].append(symbol.name)

        return symbols

    def _extract_swift_class(self, line: str, file: str) -> Optional[SymbolInfo]:
        """提取 Swift 类信息"""
        # 提取类名（支持新旧两种格式）
        name_match = re.search(r'"([A-Za-z_][A-Za-z0-9_]*)"', line) or \
                     re.search(r'name=([A-Za-z_][A-Za-z0-9_]*)', line)

        if not name_match:
            return None

        class_name = name_match.group(1)

        # 提取位置信息
        location = self._extract_location(line, file)
        modifiers = self._extract_modifiers(line)

        return SymbolInfo(
            name=class_name,
            type='class',
            file=file or "",
            line=location[0] if location else 0,
            column=location[1] if location else 0,
            modifiers=modifiers
        )

    def _extract_swift_struct(self, line: str, file: str) -> Optional[SymbolInfo]:
        """提取 Swift 结构体信息"""
        name_match = re.search(r'"([A-Za-z_][A-Za-z0-9_]*)"', line) or \
                     re.search(r'name=([A-Za-z_][A-Za-z0-9_]*)', line)

        if not name_match:
            return None

        struct_name = name_match.group(1)
        location = self._extract_location(line, file)
        modifiers = self._extract_modifiers(line)

        return SymbolInfo(
            name=struct_name,
            type='struct',
            file=file or "",
            line=location[0] if location else 0,
            column=location[1] if location else 0,
            modifiers=modifiers
        )

    def _extract_swift_enum(self, line: str, file: str) -> Optional[SymbolInfo]:
        """提取 Swift 枚举信息"""
        name_match = re.search(r'"([A-Za-z_][A-Za-z0-9_]*)"', line) or \
                     re.search(r'name=([A-Za-z_][A-Za-z0-9_]*)', line)

        if not name_match:
            return None

        enum_name = name_match.group(1)
        location = self._extract_location(line, file)
        modifiers = self._extract_modifiers(line)

        return SymbolInfo(
            name=enum_name,
            type='enum',
            file=file or "",
            line=location[0] if location else 0,
            column=location[1] if location else 0,
            modifiers=modifiers
        )

    def _extract_swift_protocol(self, line: str, file: str) -> Optional[SymbolInfo]:
        """提取 Swift 协议信息"""
        name_match = re.search(r'"([A-Za-z_][A-Za-z0-9_]*)"', line) or \
                     re.search(r'name=([A-Za-z_][A-Za-z0-9_]*)', line)

        if not name_match:
            return None

        protocol_name = name_match.group(1)
        location = self._extract_location(line, file)
        modifiers = self._extract_modifiers(line)

        return SymbolInfo(
            name=protocol_name,
            type='protocol',
            file=file or "",
            line=location[0] if location else 0,
            column=location[1] if location else 0,
            modifiers=modifiers
        )

    def _extract_swift_function(self, line: str, file: str, current_class: Optional[str]) -> Optional[SymbolInfo]:
        """提取 Swift 函数/方法信息"""
        name_match = re.search(r'"([A-Za-z_][A-Za-z0-9_]*(?:\([^)]*\))?)"', line) or \
                     re.search(r'name=([A-Za-z_][A-Za-z0-9_]*)', line)

        if not name_match:
            return None

        func_name = name_match.group(1)
        location = self._extract_location(line, file)
        modifiers = self._extract_modifiers(line)

        return SymbolInfo(
            name=func_name,
            type='method' if current_class else 'function',
            file=file or "",
            line=location[0] if location else 0,
            column=location[1] if location else 0,
            modifiers=modifiers,
            parent=current_class
        )

    def _extract_swift_property(self, line: str, file: str, current_class: Optional[str]) -> Optional[SymbolInfo]:
        """提取 Swift 属性信息"""
        name_match = re.search(r'"([A-Za-z_][A-Za-z0-9_]*)"', line) or \
                     re.search(r'name=([A-Za-z_][A-Za-z0-9_]*)', line)

        if not name_match:
            return None

        prop_name = name_match.group(1)
        location = self._extract_location(line, file)
        modifiers = self._extract_modifiers(line)

        return SymbolInfo(
            name=prop_name,
            type='property',
            file=file or "",
            line=location[0] if location else 0,
            column=location[1] if location else 0,
            modifiers=modifiers,
            parent=current_class
        )

    def _extract_location(self, line: str, file: str) -> tuple:
        """
        提取位置信息

        Args:
            line: AST 行
            file: 文件路径

        Returns:
            (line_number, column_number)
        """
        # 尝试提取行号:列号
        loc_match = re.search(r':(\d+):(\d+)', line)
        if loc_match:
            return (int(loc_match.group(1)), int(loc_match.group(2)))
        return (0, 0)

    def _extract_modifiers(self, line: str) -> List[str]:
        """
        提取修饰符

        Args:
            line: AST 行

        Returns:
            修饰符列表
        """
        modifiers = []
        if 'public' in line:
            modifiers.append('public')
        if 'private' in line:
            modifiers.append('private')
        if 'internal' in line:
            modifiers.append('internal')
        if 'fileprivate' in line:
            modifiers.append('fileprivate')
        if 'static' in line:
            modifiers.append('static')
        if 'final' in line:
            modifiers.append('final')
        return modifiers

    def _extract_swift_symbols_by_regex(self, files: List[str]) -> Dict:
        """
        使用正则表达式提取 Swift 符号（回退方法）

        Args:
            files: 文件列表

        Returns:
            符号字典
        """
        symbols = self._init_symbol_dict(files)

        for file_path in files:
            try:
                with open(file_path, 'r', encoding='utf-8') as f:
                    content = f.read()

                # 提取类
                for match in re.finditer(r'class\s+([A-Za-z_][A-Za-z0-9_]*)', content):
                    class_name = match.group(1)
                    symbols['classes'][class_name] = SymbolInfo(
                        name=class_name,
                        type='class',
                        file=file_path,
                        line=content[:match.start()].count('\n') + 1,
                        column=0
                    )
                    symbols['file_symbols'][file_path].append(class_name)

                # 提取结构体
                for match in re.finditer(r'struct\s+([A-Za-z_][A-Za-z0-9_]*)', content):
                    struct_name = match.group(1)
                    symbols['structs'][struct_name] = SymbolInfo(
                        name=struct_name,
                        type='struct',
                        file=file_path,
                        line=content[:match.start()].count('\n') + 1,
                        column=0
                    )
                    symbols['file_symbols'][file_path].append(struct_name)

                # 提取枚举
                for match in re.finditer(r'enum\s+([A-Za-z_][A-Za-z0-9_]*)', content):
                    enum_name = match.group(1)
                    symbols['enums'][enum_name] = SymbolInfo(
                        name=enum_name,
                        type='enum',
                        file=file_path,
                        line=content[:match.start()].count('\n') + 1,
                        column=0
                    )
                    symbols['file_symbols'][file_path].append(enum_name)

                # 提取协议
                for match in re.finditer(r'protocol\s+([A-Za-z_][A-Za-z0-9_]*)', content):
                    protocol_name = match.group(1)
                    symbols['protocols'][protocol_name] = SymbolInfo(
                        name=protocol_name,
                        type='protocol',
                        file=file_path,
                        line=content[:match.start()].count('\n') + 1,
                        column=0
                    )
                    symbols['file_symbols'][file_path].append(protocol_name)

                # 提取函数/方法
                for match in re.finditer(r'func\s+([A-Za-z_][A-Za-z0-9_]*)', content):
                    func_name = match.group(1)
                    symbols['functions'][func_name] = SymbolInfo(
                        name=func_name,
                        type='function',
                        file=file_path,
                        line=content[:match.start()].count('\n') + 1,
                        column=0
                    )
                    symbols['file_symbols'][file_path].append(func_name)

                # 提取属性（var/let）
                for match in re.finditer(r'(?:var|let)\s+([A-Za-z_][A-Za-z0-9_]*)', content):
                    prop_name = match.group(1)
                    symbols['properties'][prop_name] = SymbolInfo(
                        name=prop_name,
                        type='property',
                        file=file_path,
                        line=content[:match.start()].count('\n') + 1,
                        column=0
                    )
                    symbols['file_symbols'][file_path].append(prop_name)

            except Exception as e:
                self.logger.log_warning(f"正则提取失败 {file_path}: {e}")

        return symbols
